#include <inctypes.h>
#include <incctrls.h>

/* =================== 硬件相关 =================== */

/*********************************************************************************
 * 
 * drv_s3c2440_rtc_init - RTC 设备初始化
 * 
 * DESCRIPTION
 */

static void drv_s3c2440_rtc_init (void)
    {
    u8_t cfgreg;

    /* RTC控制使能 */

    hal_io8_write (S3C2440_REG_RTCCON, S3C2440_RTCCTL_EN);

    /* 设置节拍时间计数值为 127 并 开启RTC节拍中断 */

    cfgreg = (u8_t) (S3C2440_RTCTICK_INT_EN | 127);
    hal_io8_write (S3C2440_REG_TICNT, cfgreg);

    return;
    }

/*********************************************************************************
 * 
 * drv_s3c2440_rtc_reset - RTC 重置时间寄存器
 * 
 * DESCRIPTION
 */

static drvstus_t drv_s3c2440_rtc_reset 
    (
    time_t * time
    )
    {
    uint_t year, mon, day, date, hour, min, sec;

    /* 提取年月日周时分秒数值 */

    year = time->year - 2000; /* 年 (RTC 年寄存器值从 2000 开始) */
    mon  = time->mon;         /* 月 */
    day  = time->day;         /* 星期 */
    date = time->date;        /* 日 */
    hour = time->hour;        /* 时 */
    min  = time->min;         /* 分 */
    sec  = time->sec;         /* 秒 */

    /* BIN 转 BCD码 */

    BIN_TO_BCD (year);      /* 年 */
    BIN_TO_BCD (mon);       /* 月 */
    BIN_TO_BCD (day);       /* 星期 */
    BIN_TO_BCD (date);      /* 日 */
    BIN_TO_BCD (hour);      /* 时 */
    BIN_TO_BCD (min);       /* 分 */
    BIN_TO_BCD (sec);       /* 秒 */

    /* RTC控制使能 */

    hal_io8_write (S3C2440_REG_RTCCON, S3C2440_RTCCTL_EN);

    /* 设置年月日周时分秒寄存器数值 */

    hal_io8_write (S3C2440_REG_BCDYEAR, (u8_t) year);  /* 年 */
    hal_io8_write (S3C2440_REG_BCDMON, (u8_t) mon);    /* 月 */
    hal_io8_write (S3C2440_REG_BCDDAY, (u8_t) day);    /* 星期 */
    hal_io8_write (S3C2440_REG_BCDDATE, (u8_t) date);  /* 日 */
    hal_io8_write (S3C2440_REG_BCDHOUR, (u8_t) hour);  /* 时 */
    hal_io8_write (S3C2440_REG_BCDMIN, (u8_t) min);    /* 分 */
    hal_io8_write (S3C2440_REG_BCDSEC, (u8_t) sec);    /* 秒 */

    return OK;
    }

/*********************************************************************************
 * 
 * drv_s3c2440_rtc_set_irqtick - RTC 设置中断请求节拍
 * 
 * DESCRIPTION
 */

static drvstus_t drv_s3c2440_rtc_set_irqtick
    (
    uint_t freq
    )
    {
    u8_t cfgreg;

    /* RTC控制使能 */

    hal_io8_write (S3C2440_REG_RTCCON, S3C2440_RTCCTL_EN);

    /* 设置 RTC 分频值 并 使能 RTC节拍 中断 */

    cfgreg = (u8_t) ((freq & 0x3F) | S3C2440_RTCTICK_INT_EN);
    hal_io8_write (S3C2440_REG_TICNT, cfgreg);

    return OK;
    }

/*********************************************************************************
 * 
 * drv_s3c2440_rtc_set_alarm - RTC 设置闹钟 (支支持 时分秒 闹钟)
 * 
 * DESCRIPTION
 */

static drvstus_t drv_s3c2440_rtc_set_alarm
    (
    device_t * devp,
    void *     iopack,
    time_t *   alarm
    )
    {
    uint_t year_temp, mon_temp, date_temp;
    uint_t hour, min, sec;                  /* 闹钟只支持设置 时分秒 */
    uint_t cfgreg = 0, enstus;

    /* 提取 时分秒数值 */

    hour = alarm->hour;        /* 时 */
    min  = alarm->min;         /* 分 */
    sec  = alarm->sec;         /* 秒 */

    /* 获取时分秒寄存器数值 */

    year_temp = hal_io8_read (S3C2440_REG_BCDYEAR);    /* 年 */
    mon_temp  = hal_io8_read (S3C2440_REG_BCDMON);     /* 月 */
    date_temp  = hal_io8_read (S3C2440_REG_BCDDAY);    /* 星期 */

    /* BIN 转 BCD 码 */

    BIN_TO_BCD (hour);         /* 时 */
    BIN_TO_BCD (min);          /* 分 */
    BIN_TO_BCD (sec);          /* 秒 */

    /* RTC控制使能 */

    hal_io8_write (S3C2440_REG_RTCCON, S3C2440_RTCCTL_EN);

    /* 设置年月日时分秒 闹钟寄存器数值 */

    hal_io8_write (S3C2440_REG_ALMYEAR, (u8_t) year_temp);   /* 年 */
    hal_io8_write (S3C2440_REG_ALMMON, (u8_t) mon_temp);     /* 月 */
    hal_io8_write (S3C2440_REG_ALMDATE, (u8_t) date_temp);   /* 日 */
    hal_io8_write (S3C2440_REG_ALMHOUR, (u8_t) hour);        /* 日 */
    hal_io8_write (S3C2440_REG_ALMMIN, (u8_t) min);          /* 时 */
    hal_io8_write (S3C2440_REG_ALMSEC, (u8_t) sec);          /* 分 */

    /* 把 IO包挂在到设备服务等待链表上 */

    if (krl_dev_add_request (devp, (objnode_t *) iopack) == ERROR)
        {
        krl_system_die ("drv_s3c2440_rtc_set_alarm err\r\n");
        return ERROR;
        }

    /* 判断开启哪些定时功能 */

    if ((hour == 0) && (min == 0) && (sec == 0))             /* 000 */
        {
        enstus = FALSE; /* 闹钟功能开关 */
        cfgreg = 0;     /* 需要使能的定时功能 */
        }
    else if ((hour == 0) && (min == 0) && (sec != 0))         /* 001 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_SEC_EN;
        }
    else if ((hour == 0) && (min != 0) && (sec == 0))         /* 010 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_MIN_EN;
        }
    else if ((hour == 0) && (min != 0) && (sec != 0))         /* 011 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_MIN_EN | S3C2440_RTCALM_SEC_EN;
        }
    else if ((hour != 0) && (min == 0) && (sec == 0))         /* 100 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_HOUR_EN;
        }
    else if ((hour != 0) && (min == 0) && (sec != 0))         /* 101 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_HOUR_EN | S3C2440_RTCALM_SEC_EN;
        }
    else if ((hour != 0) && (min != 0) && (sec == 0))         /* 110 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_HOUR_EN | S3C2440_RTCALM_MIN_EN;
        }
    else if ((hour != 0) && (min != 0) && (sec != 0))         /* 111 */
        {
        enstus = TRUE;
        cfgreg = S3C2440_RTCALM_HOUR_EN | S3C2440_RTCALM_MIN_EN | S3C2440_RTCALM_SEC_EN;
        }

    /* 判断是否成功设置闹钟 */

    if (enstus == TRUE)
        {
        cfgreg |= S3C2440_RTCALM_INT_EN;    /* 使能 RTC闹钟 中断 */

        /* 设置闹钟状态和指定开启哪些定时功能 */

        hal_io8_write (S3C2440_REG_RTCALM, cfgreg);

        /* 设置成功 让进程睡眠等待 */

        krl_dev_wait_request (devp, (objnode_t *) iopack);

        return OK;
        }

    /* 设置闹钟失败 */

    return ERROR;
    }

/* =================== 中断服务函数 =================== */

/*********************************************************************************
 * 
 * drv_rtcalm_isrhandle - RTC 设备驱动程序闹钟中断回调函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtcalm_isrhandle
    (
    uint_t ifdidx,
    void * devp,
    void * sframe
    )
    {
    objnode_t * request;

    /* 判断该设备有无等待的IO包 */

    if (((device_t *) devp)->rqnbr > 0)
        {

        /* 获取具体的 IO 包 */

        if (krl_dev_return_request ((device_t *) devp, IOIF_CODE_IOCTL, &request) == ERROR)
            {
            krl_system_die ("drv_rtcalm_isrhandle no request\r\n");
            return ERROR;
            }
        
        /* 完成服务, 唤醒休眠的进程 */

        if (krl_dev_complete_request ((device_t *) devp, request) == ERROR)
            {
            krl_system_die ("drv_rtcalm_isrhandle no complete request\r\n");
            return ERROR;
            }
        }

    return OK;
    }

/*********************************************************************************
 * 
 * drv_rtctick_isrhandle - RTC 设备驱动程序节拍中断回调函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtctick_isrhandle
    (
    uint_t ifdidx,
    void * devp,
    void * sframe
    )
    {
    uint_t year, mon, day, date, hour, min, sec;

    /* 获取年月日周时分秒寄存器数值 */

    year = hal_io8_read (S3C2440_REG_BCDYEAR);    /* 年 */
    mon  = hal_io8_read (S3C2440_REG_BCDMON);     /* 月 */
    day  = hal_io8_read (S3C2440_REG_BCDDAY);     /* 星期 */
    date = hal_io8_read (S3C2440_REG_BCDDATE);    /* 日 */
    hour = hal_io8_read (S3C2440_REG_BCDHOUR);    /* 时 */
    min  = hal_io8_read (S3C2440_REG_BCDMIN);     /* 分 */
    sec  = hal_io8_read (S3C2440_REG_BCDSEC);     /* 秒 */

    /* BCD码 转 BIN */

    BCD_TO_BIN (year); 
    BCD_TO_BIN (mon);
    BCD_TO_BIN (day);
    BCD_TO_BIN (date);
    BCD_TO_BIN (hour);
    BCD_TO_BIN (min);
    BCD_TO_BIN (sec);

    /* 调用系统更新函数更新系统时间 (RTC 年寄存器值从 2000 开始) */

    krl_time_update (year + 2000, mon, day, date, hour, min, sec);

    return OK;
    }

/* =================== IO 分发函数 =================== */

/*********************************************************************************
 * 
 * drv_rtc_open - rtc 设备驱动程序的打开函数 
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_open
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_close - rtc 设备驱动程序关闭函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_close
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_read - rtc 设备驱动程序读函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_read
    (
    device_t * devp,
    void *     iopack
    )
    {
    uint_t retlen;
    buf_t  retbuf;
    uint_t year, mon, day, date, hour, min, sec;

    /* 获取 IO 包中的缓冲区地址和缓冲区长度 */

    if (krl_return_reqstparam (iopack, &retbuf, NULL, &retlen, NULL, NULL, NULL) == ERROR)
        {
        return ERROR;   /* 获取失败 */
        }

    /* 检查缓冲区地址和缓冲区长度是否符合要求 */

    if ((retbuf == NULL) || (retlen < sizeof (time_t)))
        {
        return ERROR;   /* 有效性检测失败 */
        }

    /* 获取年月日周时分秒寄存器数值 */

    year = hal_io8_read (S3C2440_REG_BCDYEAR);    /* 年 */
    mon  = hal_io8_read (S3C2440_REG_BCDMON);     /* 月 */
    day  = hal_io8_read (S3C2440_REG_BCDDAY);     /* 星期 */
    date = hal_io8_read (S3C2440_REG_BCDDATE);    /* 日 */
    hour = hal_io8_read (S3C2440_REG_BCDHOUR);    /* 时 */
    min  = hal_io8_read (S3C2440_REG_BCDMIN);     /* 分 */
    sec  = hal_io8_read (S3C2440_REG_BCDSEC);     /* 秒 */

    /* BCD码 转 BIN */

    BCD_TO_BIN (year);
    BCD_TO_BIN (mon);
    BCD_TO_BIN (day);
    BCD_TO_BIN (date);
    BCD_TO_BIN (hour);
    BCD_TO_BIN (min);
    BCD_TO_BIN (sec);

    /* 缓冲区的地址强制转换成 time_t 结构示例变量的地址 */

    time_t * time = (time_t *) retbuf;

    /* 把时间数据写入缓冲区中 */

    time->year = year + 2000;  /* 年 (RTC 年寄存器值从 2000 开始) */
    time->mon  = mon;          /* 月 */
    time->day  = day;          /* 星期 */
    time->date = date;         /* 日 */
    time->hour = hour;         /* 时 */
    time->min  = min;          /* 分 */
    time->sec  = sec;          /* 秒 */

    return OK;
    }

/*********************************************************************************
 * 
 * drv_rtc_write - rtc 设备驱动程序写入函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_write
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_lseek - rtc 设备驱动程序调整读写位置函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_lseek
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_ioctl - rtc 设备驱动程序控制函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_ioctl
    (
    device_t * devp,
    void *     iopack
    )
    {
    drvstus_t          retstus = OK;    /* 初始化为OK */
    enum RTC_IOCTL_CMD iocmd;
    buf_t              retbuf = NULL;

    /* 获取IO包中的缓冲区地址和控制码 */

    if (krl_return_reqstparam (iopack, &retbuf, NULL, NULL, (uint_t *) &iocmd, NULL, NULL) == ERROR)
        {
        return ERROR;
        }

    /* 检查 buf 有效性 */

    if (retbuf == NULL)
        {
        return ERROR;
        }

    /* 根据 IOCMD 执行不同功能 */

    switch (iocmd)
        {
        case RTC_IOCTL_SETTIME:     /* 设置时间 */
            if (drv_s3c2440_rtc_reset ((time_t *) retbuf) == ERROR)
                {
                retstus = ERROR;
                }
            break;

        case RTC_IOCTL_SETIRQTICK:  /* 设置中断请求节拍 */
            if (drv_s3c2440_rtc_set_irqtick (*((uint_t *) retbuf)) == ERROR)
                {
                retstus = ERROR;
                }
            break;

        case RTC_IOCTL_SETALARM:    /* 设置闹钟 */
            if (drv_s3c2440_rtc_set_alarm (devp, iopack, (time_t *) retbuf) == ERROR)
                {
                retstus = ERROR;
                }
            break;
    
        default:
            retstus = ERROR;        /* 未识别错误码 */
            break;
        }

    return retstus;
    }

/*********************************************************************************
 * 
 * drv_rtc_start - rtc 设备驱动程序开始函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_start
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_stop - rtc 设备驱动程序停止函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_stop
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_powerset - rtc 设备驱动程序的设置电源函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_powerset
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_enumdev - rtc 设备驱动程序枚举设备函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_enumdev
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_flush - rtc 设备驱动程序刷新缓存函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_flush
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }

/*********************************************************************************
 * 
 * drv_rtc_shutdown - rtc 设备驱动程序关机函数
 * 
 * DESCRIPTION
 */

static drvstus_t drv_rtc_shutdown
    (
    device_t * devp,
    void *     iopack
    )
    {
    return ERROR;
    }


/* =================== 设置相关结构体的域 =================== */

/*********************************************************************************
 * 
 * drv_rtc_set_driver - 填写 driver_t 结构示例变量的相关域
 * 
 * DESCRIPTION
 */

static void drv_rtc_set_driver
    (
    driver_t * drvp
    )
    {
    
    /* 设置 I/O 功能分发函数 */

    drvp->dipfunc[IOIF_CODE_OPEN]     = drv_rtc_open;
    drvp->dipfunc[IOIF_CODE_CLOSE]    = drv_rtc_close;
    drvp->dipfunc[IOIF_CODE_READ]     = drv_rtc_read;
    drvp->dipfunc[IOIF_CODE_WRITE]    = drv_rtc_write;
    drvp->dipfunc[IOIF_CODE_LSEEK]    = drv_rtc_lseek;
    drvp->dipfunc[IOIF_CODE_IOCTL]    = drv_rtc_ioctl;
    drvp->dipfunc[IOIF_CODE_START]    = drv_rtc_start;
    drvp->dipfunc[IOIF_CODE_STOP]     = drv_rtc_stop;
    drvp->dipfunc[IOIF_CODE_POWERSET] = drv_rtc_powerset;
    drvp->dipfunc[IOIF_CODE_ENUMDEV]  = drv_rtc_enumdev;
    drvp->dipfunc[IOIF_CODE_FLUSH]    = drv_rtc_flush;
    drvp->dipfunc[IOIF_CODE_SHUTDOWN] = drv_rtc_shutdown;

    /* 设置驱动名 */

    drvp->name = "rtc_drv";

    return;
    }

/*********************************************************************************
 * 
 * drv_rtc_set_device - 填写 device_t 结构示例变量的相关域
 * 
 * DESCRIPTION
 */

static void drv_rtc_set_device
    (
    device_t * devp,
    driver_t * drvp
    )
    {

    /* 填写设备标志, 独占使用设备 */

    devp->flag = DEV_ATTRFLG_SHARE;

    /* 填写设备状态 (普通状态, 设备可用) */

    devp->status = DEV_ATTRSTS_NORMAL;

    /* 填写设备类型 (主设备号, 次设备号) */

    devp->id.majtype = DEVICE_CLASSITY_RTC; /* rtc 类型设备 */
    devp->id.subtype = 0;                       /* 0 */

    /* 填写设备序号 */

    devp->id.index = 0;                         /* 0 */

    /* 填写设备名 */

    devp->name = "rtc0";

    return;
    }

/* =================== 驱动程序框架 =================== */

/*********************************************************************************
 * 
 * drv_systick_entry - rtc 设备驱动程序入口函数
 * 
 * DESCRIPTION
 */

drvstus_t drv_rtc_entry
    (
    driver_t * drvp,
    uint_t     value,
    void *     param
    )
    {

    /* 判断参数有效性 */

    if (drvp == NULL)
        {
        return ERROR;
        }

    /* 创建 device_t 结构的实例变量 */

    device_t * devp = dev_create_devicedsc ();

    if (devp == NULL)
        {
        return ERROR;
        }

    /* 填写 driver_t 和 device_t 的域 */

    drv_rtc_set_driver (drvp);
    drv_rtc_set_device (devp, drvp);

    /* 将设备添加进驱动管理链表 */

    if (dev_add_device_to_driver (devp, drvp) == ERROR)
        {
        
        /* 设备加入驱动失败, 释放资源 (删除设备数据结构的实例变量) */

        goto return_step;
        }

    /* 将设备注册进内核系统 */

    if (krl_dev_register_device (devp) ==  ERROR)
        {

        /* 设备注册进内核失败, 释放资源 (删除设备数据结构的实例变量) */

        goto return_step;
        }

    /* 注册RTC闹钟中断回调函数 */

    if (krl_dev_register_isrhandle (devp, drv_rtcalm_isrhandle, 
                                    MAJINT_DESCIDX (S3C2440_RTCALM_IRQIDX)) == ERROR)
        {

        /* 设置中断回调失败, 释放资源 (删除设备数据结构的实例变量) */

        goto return_step;
        }

    /* 注册RTC节拍中断回调函数 */

    if (krl_dev_register_isrhandle (devp, drv_rtctick_isrhandle, 
                                    MAJINT_DESCIDX (S3C2440_RTCTICK_IRQIDX)) == ERROR)
        {

        /* 设置中断回调失败, 释放资源 (删除设备数据结构的实例变量) */

        goto return_step;
        }

    /* 初始化硬件 (rtc) */

    drv_s3c2440_rtc_init ();

    /* 
     * 使能 RTC闹钟 硬件中断 
     * S3C2440_RTCALM_IRQIDX 是物理中断屏蔽位, 需要经过 MAJINT_DESCIDX 
     * 宏转换后才是内核 所需要的
     */

    if (intrpt_enable_irq (MAJINT_DESCIDX (S3C2440_RTCALM_IRQIDX)) == ERROR)
        {
        
        /* 使能RTC中断失败 */

        goto return_step;
        }

    /* 
     * 使能 RTC节拍 硬件中断 
     * S3C2440_RTCTICK_IRQIDX 是物理中断屏蔽位, 需要经过 MAJINT_DESCIDX 
     * 宏转换后才是内核 所需要的
     */

    if (intrpt_enable_irq (MAJINT_DESCIDX (S3C2440_RTCTICK_IRQIDX)) == ERROR)
        {
        
        /* 使能RTC中断失败 */

        goto return_step;
        }

    return OK;

return_step:

    /* 删除设备数据结构的实例变量 */   

    if (dev_delete_devicedsc (devp) == FALSE)
        {
        return ERROR; /* 删除失败 */
        }

    return ERROR;     /* 返回失败 */
    }

/*********************************************************************************
 * 
 * drv_systick_exit - rtc 设备驱动程序出口函数
 * 
 * DESCRIPTION
 */

drvstus_t drv_rtc_exit
    (
    driver_t * drvp,
    uint_t     value,
    void *     param
    )
    {
    return ERROR;
    }

